package test;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.TreeMap;

/**
 * Created by Connor Madden.
 * User: Connor Madden
 * Date: 20/08/11
 * Time: 4:36 PM
 */
public class Scheduler {

    LinkedList<Integer> disembarkLoc;
    TreeMap<Integer, ArrayList<Integer>> callLoc;

    public Scheduler() {
        disembarkLoc = new LinkedList<Integer>();
        callLoc = new TreeMap<Integer, ArrayList<Integer>>();
    }

    public boolean hasDestination() {
        return !disembarkLoc.isEmpty() || !callLoc.isEmpty();
    }

    public boolean isStop(int loc, int dir) {
        return disembarkLoc.contains(loc) || (callLoc.containsKey(loc) && callLoc.get(loc).contains(dir));
    }

    public int getStep(Lift lift) {
        int liftLoc = lift.getLoc();
        int liftDir = lift.getLastDir();
        // check there is outstanding call from a floor in the same direction the elevator last moved
        if (this.hasNextCall(liftLoc, liftDir) && liftDir != 0) return liftDir;
            // System check elevator has requested stops
        else if (!disembarkLoc.isEmpty()) {
            int l = disembarkLoc.peek();
            l -= lift.getLoc();
            if (l == 0) return 0;
            else return l / Math.abs(l);
            // System check there is outstanding call from a floor in the opposite direction the elevator last moved
        } else if (this.hasNextCall(liftLoc, liftDir * -1) && liftDir != 0) return liftDir * -1;
        // Check if there is outstanding calls in either direction;
        // so if you have no direction it will not freeze
        if (liftDir == 0) {
            if (this.hasNextCall(liftLoc, 1)) return 1;
            else if (this.hasNextCall(liftLoc, -1)) return -1;
        }
        return 0;
    }

    /**
     * Check there is no outstanding call from a location in the same direction <tt>dir</tt>
     * starting from position <tt>loc</tt>
     *
     * @param loc the location
     * @param dir the direction
     * @return <tt>true</tt> if there is an outstanding call from a location in the same direction <tt>dir</tt>
     *         starting from position <tt>loc</tt>
     */
    public boolean hasNextCall(int loc, int dir) {
        if (!callLoc.isEmpty()) {
            int[] keys = {callLoc.firstKey(), callLoc.lastKey()};
            for (int aKey : keys) {
                int x = (aKey - loc);
                // x / |x|
                if (dir == x / Math.abs(x)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * @param loc the location
     * @param dir the direction
     * @return <tt>true</tt> if changed as a result of add
     */
    public boolean addCall(int loc, int dir) {
        ArrayList<Integer> call;
        if (callLoc.containsKey(loc)) {
            if (callLoc.get(loc).contains(dir)) return false;
            else {
                call = callLoc.get(loc);
                call.add(dir);

            }
        } else {
            call = new ArrayList<Integer>(2);
            call.add(dir);
        }
        callLoc.put(loc, call);
        return true;
    }

    public boolean removeCall(int loc, int dir) {
        if (callLoc.containsKey(loc)) {
            if (callLoc.get(loc).contains(dir)) {
                ArrayList<Integer> call = callLoc.get(loc);
                call.remove(dir);
                /*
                * if "call" is empty remove key "loc" from "callLoc"
                * else make "call" the value of key "loc" in "callLoc"
                */
                if (call.isEmpty()) callLoc.remove(loc);
                else callLoc.put(loc, call);
                return true;
            } else return false;
        } else return false;
    }

    public boolean addDisembark(int loc) {
        return disembarkLoc.offer(loc);
    }

    public boolean removeDisembark(int liftLoc) {
        return disembarkLoc.remove((Integer) liftLoc);
    }
}
